package se.webbzon.boltzmann.minigame.botany;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import se.webbzon.boltzmann.configuration.Difficulty;
import se.webbzon.boltzmann.minigame.chemistry.SandBoxMolecule;
import se.webbzon.boltzmann.minigame.effect.SandBoxFeedback;
import se.webbzon.boltzmann.minigame.effect.SandBoxFeedback.Symbol;
import se.webbzon.boltzmann.minigame.sandbox.ReadOnlySandBox;
import se.webbzon.boltzmann.minigame.sandbox.SandBox;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxContainer;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxIcon;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxItem;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxListener;
import se.webbzon.boltzmann.sound.SoundUtils;

public abstract class Cycle implements SandBoxItem {
	
	// A collection of all the cycle listeners
	private final Collection<CycleListener> listeners;
	
	// A collection of all the items in this cycle
	private final Collection<SandBoxItem> items;
	
	// A map containing the solution of the cycle
	private final Map<SandBoxContainer,Class<? extends SandBoxMolecule>> solution;
	
	// A sand box listener
	private final SandBoxListener listener;
	
	// The number of correct items
	private int correct = 0;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public Cycle() {
		listeners = new ArrayList<CycleListener>();
		items = new ArrayList<SandBoxItem>();
		listener = new SandBoxListenerImpl();
		solution = new HashMap<SandBoxContainer,Class<? extends SandBoxMolecule>>();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Returns the difficulty of this cycle. **/
	public abstract Difficulty getDifficulty(); 
	
	/** Adds a listener to this cycle. **/
	public boolean addListener(CycleListener listener) {
		return listeners.add(listener);
	}
	
	/** Removes a listener from this cycle. **/
	public boolean removeListener(CycleListener listener) {
		return listeners.remove(listener);
	}
	
	/** Clears all listeners from this cycle. **/
	public void clearListeners() {
		listeners.clear();
	}
	
	/** Adds a sand box item to this cycle. **/
	public boolean addItem(SandBoxItem item) {
		return items.add(item);
	}
	
	/** Removes a sand box item from this cycle. **/
	public boolean removeItem(SandBoxItem item) {
		return items.remove(item);
	}
	
	/** Clears all sand box items from this cycle. **/
	public void clearItems() {
		items.clear();
	}

	@Override public void onAttach(SandBox box) {
		box.addListener(listener);
		correct = 0;
		onCreate(box);
		for (SandBoxItem item : items)
			box.addItem(item);
	}

	@Override public void onDetach(SandBox box) {
		for (SandBoxItem item : items)
			box.removeItem(item);
		onDestroy(box);
		box.removeListener(listener);
	}

	@Override public boolean draggable() {
		return false;
	}

	@Override public boolean droppable() {
		return false;
	}

	@Override public void setPosition(double x, double y) {};

	@Override public double getX() {
		return 0;
	}

	@Override public double getY() {
		return 0;
	}

	@Override public double getWidth() {
		return 0;
	}

	@Override public double getHeight() {
		return 0;
	}

	@Override public SandBoxIcon createIcon() {
		return null;
	}
	
	/*============================================================
	Protected Methods
	============================================================*/
	
	/** This method is called when the cycle is created. **/
	protected abstract void onCreate(ReadOnlySandBox box);
	
	/** This method is called when the cycle is destroyed. **/
	protected abstract void onDestroy(ReadOnlySandBox box);
	
	/** Adds a solution to the provided sand box container. **/
	protected void addSolution(SandBoxContainer c, Class<? extends SandBoxMolecule> clazz) {
		solution.put(c, clazz);
	}
	
	/** Removes a solution from the provided sand box container. **/
	protected void removeSolution(SandBoxContainer c) {
		solution.remove(c);
	}
	
	/** Clears the solution of this cycle. **/
	protected void clearSolution() {
		solution.clear();
	}
	
	/*============================================================
	Private Classes
	============================================================*/
	
	private final class SandBoxListenerImpl implements SandBoxListener {

		@Override public void onInvoke(SandBox box, double x, double y) {};

		@Override public void onPick(SandBox box, SandBoxItem item, SandBoxContainer container) {
			Class<?> clazz = solution.get(container);
			if (item.getClass() == clazz) {
				final int points = -750;
				correct--;
				box.getScore().addPoints(-750);
				SandBoxItem score = SandBoxFeedback.createScore(box, points);
				score.setPosition(container.getX(), container.getY());
				box.addItem(score);
			}
		}

		@Override public void onDrop(SandBox box, SandBoxItem item, SandBoxContainer container) {
			Class<?> clazz = solution.get(container);
			if (item.getClass() == clazz) {
				final int points = 750;
				correct++;
				box.getScore().addPoints(points);
				SoundUtils.playSound("positive");
				SandBoxItem symbol = SandBoxFeedback.createSymbol(box, Symbol.POSITIVE);
				SandBoxItem score = SandBoxFeedback.createScore(box, points);
				symbol.setPosition(container.getX(), container.getY());
				score.setPosition(container.getX(), container.getY());
				box.addItem(symbol);
				box.addItem(score);
				if (correct == solution.size())
					for (CycleListener listener : listeners.toArray(new CycleListener[0]))
						listener.onComplete(Cycle.this);
			} else if (container != null) {
				final int points = -1000;
				box.getScore().addPoints(points);
				SoundUtils.playSound("negative");
				SandBoxItem symbol = SandBoxFeedback.createSymbol(box, Symbol.NEGATIVE);
				SandBoxItem score = SandBoxFeedback.createScore(box, points);
				symbol.setPosition(container.getX(), container.getY());
				score.setPosition(container.getX(), container.getY());
				box.addItem(symbol);
				box.addItem(score);
			}
		}
		
	}

}
